package org.example.Stream;

import org.junit.Test;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/** @ClassName: StreamDemo2 @Author NeySun @Description: TODO @Version 1.0 @Date 2022-03-04 */
public class StreamDemo2 {
    public static void main(String[] args) {
        // 单列集合
        List<Author> authors = getAuthors();
        Stream<Author> stream = authors.stream();

        // 数组：`Arrays.stream(数组) `或者使用`Stream.of`来创建
        Integer[] arr = {1, 2, 3, 4, 5};
        Stream<Integer> stream1 = Arrays.stream(arr);
        Stream<Integer> stream2 = Stream.of(arr);

        // 双列集合：转换成单列集合后再创建
        Map<String, Integer> map = new HashMap<>();
        map.put("蜡笔小新", 19);
        map.put("黑子", 17);
        map.put("日向翔阳", 16);
        System.out.println(map);
        //
        // Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();
        // entryStream.forEach(item ->{
        // System.out.println(item);
        //        });
        List<Author> authors1 = getAuthors();
        //        打印所有姓名长度大于1的作家的姓名
        authors1.stream()
                .filter(o -> o.getName().length() > 1)
                .forEach(o -> System.out.println(o.getName()));
    }

    @Test
    // 打印所有作家的姓名
    // 可以把对流中的元素进行计算或转换。
    public void test5() {
        List<Author> authors = getAuthors();
        authors.stream().map(Author::getName).forEach(name -> System.out.println(name));
    }

    @Test
    public void test6() {
        //        打印所有作家的年龄
        List<Author> authors = getAuthors();

        authors.stream()
                .map(Author::getAge) // 转换
                .map(age -> age + 10) // map可以计算
                .forEach(System.out::println);
    }

    // 打印所有作家的姓名，并且要求其中不能有重复元素。
    @Test
    public void test7() {
        //        打印所有作家的姓名
        List<Author> authors = getAuthors();
        List<String> collect = authors.stream()
                .map(Author::getName)
                .distinct()
                .collect(Collectors.toList());
        System.out.println(collect);
    }

    // 可以对流中的元素进行排序。
    @Test
    public void test8() {
        // 对流中的元素按照年龄进行正序排序，并且要求不能有重复的元素。
        List<Author> authors = getAuthors();
        List<Author> collect = authors.stream().sorted(Comparator.comparingInt(o -> o.getAge())).collect(Collectors.toList());


        System.out.println(collect);
        //authors.stream()
        //        .distinct()
        //        .sorted((o1, o2) -> o1.getAge() - o2.getAge())
        //        .forEach(o -> System.out.println(o.getAge()));
    }

    // 对流中的元素按照年龄进行降序排序，并且要求不能有重复的元素,然后打印其中年龄最大的两个作家的姓名。
    @Test
    public void test9() {
        List<Author> authors = getAuthors();
        List<Author> collect = authors.stream().sorted((o1, o2) -> o2.getAge() - o1.getAge()).collect(Collectors.toList());


        System.out.println(collect);

        authors.stream()
                .distinct()
                .sorted((o1, o2) -> o2.getAge() - o1.getAge())
                .limit(2)
                .forEach(o -> System.out.println(o.getAge()));
    }

    @Test
    // skip
    // 	跳过流中的前n个元素，返回剩下的元素
    public void test10() {
        // 打印除了年龄最大的作家外的其他作家，要求不能有重复元素，并且按照年龄降序排序。
        List<Author> authors = getAuthors();
        authors.stream()
                .distinct()
                .sorted((o1, o2) -> o2.getAge() - o1.getAge())
                .skip(1)
                .forEach(author -> System.out.println(author.getAge()));
    }

    // flatMap
    // ​map只能把一个对象转换成另一个对象来作为流中的元素。而flatMap可以把一个对象转换成多个对象作为流中的元素。
    // 打印所有书籍的名字。要求对重复的元素进行去重。
    @Test
    public void test11() {
        List<Author> authors = getAuthors();
        List<Book> collect = authors.stream()
                .flatMap(o -> o.getBooks().stream())
                .distinct().collect(Collectors.toList());
        System.out.println(collect);
    }


    @Test
    public void test99() {
       List aaa = new ArrayList();
        for (Object o : aaa) {
            System.out.println(o);
        }

    }


    // 打印现有数据的所有分类。要求对分类进行去重。不能出现这种格式：哲学,爱情
    @Test
    public void test12() {
        // 打印现有数据的所有分类。要求对分类进行去重。不能出现这种格式：哲学,爱情     爱情
        List<Author> authors = getAuthors();
        authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .distinct()
                .flatMap(book -> Arrays.stream(book.getCategory().split(",")))
                .distinct()
                .forEach(System.out::println);
    }

    // 3.4.3 终结操作
    // forEach
    // ​对流中的元素进行遍历操作，我们通过传入的参数去指定对遍历到的元素进行什么具体操作。
    @Test
    public void test13() {
        //        输出所有作家的名字
        List<Author> authors = getAuthors();
        authors.stream()
                .map(Author::getName)
                .forEach(System.out::println);
    }

    @Test
    public void test14() {
        //        打印这些作家的所出书籍的数目，注意删除重复元素。
        List<Author> authors = getAuthors();
        long count = authors
                .stream()
                .flatMap(author -> author.getBooks().stream())
                .distinct()
                .count();
        System.out.println(count);
    }

    @Test
    public void test15() {
        //        分别获取这些作家的所出书籍的最高分和最低分并打印。
        // Stream<Author>  -> Stream<Book> ->Stream<Integer>  ->求值
        List<Author> authors = getAuthors();
        Optional<Integer> max =
                authors.stream()
                        .flatMap(author -> author.getBooks().stream())
                        .map(Book::getScore)
                        .max((o1, o2) -> o1-o2);
        System.out.println(max.get());

        Optional<Integer> min =
                authors.stream()
                        .flatMap(author -> author.getBooks().stream())
                        .map(Book::getScore)
                                .min((o1, o2) -> o1-o2);

        System.out.println(min.get());
    }

    @Test
    public void test16() {
        // collect
        // ​把当前流转换成一个集合。
        //        获取一个存放所有作者名字的List集合。
        List<Author> authors = getAuthors();
        List<String> nameList =
                authors.stream()
                        .map(Author::getName)
                        .collect(Collectors.toList());
        System.out.println(nameList);
    }

    @Test
    public void test17() {
        // 获取一个所有书名的Set集合。
        List<Author> authors = getAuthors();
        final Set<String> collect =
                authors.stream()
                        .flatMap(author -> author.getBooks().stream())
                        .map(Book::getName)
                        .collect(Collectors.toSet());

        System.out.println(collect);
    }

    @Test
    public void test18() {
        // 获取一个Map集合，map的key为作者名，value为List<Book>
        List<Author> authors = getAuthors();

        Map<String, List<Book>> collect =
                authors.stream()
                        .distinct()
                        .collect(Collectors.toMap(Author::getName, Author::getBooks));
        System.out.println();
        System.out.println(collect);
    }

    @Test
    public void test981() {
        // 获取一个Map集合，map的key为作者名，value为List<Book>
        List<Author> authors = getAuthors();
        List<Integer> collect = authors.stream().map(x -> x.getAge() + 1).collect(Collectors.toList());
        Optional<Author> any = authors.stream().filter(x -> x.getAge() > 0).findAny();
        System.out.println(any.get());
    }

    // 可以用来判断是否有任意符合匹配条件的元素，结果为boolean类型。
    @Test
    public void test19() {
        // 判断是否有年龄在29以上的作家
        List<Author> authors = getAuthors();
        boolean b = authors.stream()
                    .anyMatch(o -> o.getAge()>29);
        System.out.println(b);
    }

    // 可以用来判断是否都符合匹配条件，结果为boolean类型。如果都符合结果为true，否则结果为false。
    @Test
    public void test20() {
        // 判断是否所有的作家都是成年人
        List<Author> authors = getAuthors();
        boolean b = authors
                .stream()
                .allMatch(author -> author.getAge() >= 14);
        System.out.println(b);
    }

    @Test
    public void test21() {
        //        判断作家是否都没有超过100岁的。
        List<Author> authors = getAuthors();
        boolean b = authors.stream().noneMatch(author -> author.getAge() > 100);
        System.out.println(b);
    }

    @Test
    public void test22() {
        //        获取任意一个年龄大于18的作家，如果存在就输出他的名字
        List<Author> authors = getAuthors();
        Optional<Author> optionalAuthor =
                authors.stream().filter(author -> author.getAge() > 110)
                        .findAny();
        //Author author1 = optionalAuthor.get();
        //System.out.println(optionalAuthor.get().getName());
        //预防空指针
        optionalAuthor.ifPresent(author -> System.out.println(author.getName()));
    }

    @Test
    public void test23() {
        // 获取一个年龄最小的作家，并输出他的姓名。
        List<Author> authors = getAuthors();
        Optional<Integer> first = authors.stream().map(Author::getAge).distinct().sorted().findFirst();
        first.ifPresent(author -> System.out.println(first.get()));
    }

    @Test
    public void test24() {
        // 使用reduce求所有作者年龄的和
        List<Author> authors = getAuthors();
        Integer sum =
                authors.stream().distinct().map(author -> author.getAge()).reduce(0, Integer::sum);
        System.out.println(sum);
    }

    @Test
    public void test25() {
        // 使用reduce求所有作者中年龄的最大值
        // List<Author> authors = getAuthors();
        // Integer max =
        //     authors.stream()
        //         .map(author -> author.getAge())
        //         .reduce(Integer.MIN_VALUE, (result, element) -> result < element ? element :
        // element);
        //
        // System.out.println(max);

        //        使用reduce求所有作者中年龄的最大值
        List<Author> authors = getAuthors();
        Integer max =
                authors.stream()
                        .map(author -> author.getAge())
                        .reduce(Integer.MIN_VALUE, (result, element) -> result < element ? element : result);

        System.out.println(max);
    }

    @Test
    public void test26() {
        //        使用reduce求所有作者中年龄的最小值
        List<Author> authors = getAuthors();
        Integer min =
                authors.stream()
                        .map(author -> author.getAge())
                        .reduce(Integer.MAX_VALUE, (result, element) -> result > element ? element : result);
        System.out.println(min);
    }

    @Test
    public void test27() {
        // 使用reduce求所有作者中年龄的最小值
        List<Author> authors = getAuthors();
        Optional<Integer> minOptional =
                authors.stream()
                        .map(author -> author.getAge())
                        .reduce((result, element) -> result > element ? element : result);
        minOptional.ifPresent(age -> System.out.println(age));
    }

    @Test
    public void test28() {
        // 我们一般使用**Optional**的**静态方法ofNullable**来把数据封装成一个Optional对象。无论传入的参数是否为null都不会出现问题。
        //List<Author> authors = getAuthors();
        List<Author> authors = null;
        Optional<List<Author>> authors1 = Optional.ofNullable(authors);
        System.out.println(authors1.get());
        // 4.2.2 安全消费值
        // Optional<List<Author>> authors1 = Optional.ofNullable(authors);
        authors1.ifPresent(i -> System.out.println(i));
        // 如果你**确定一个对象不是空**的则可以使用**Optional**的**静态方法of**来把数据封装成Optional对象。
        Author author = new Author(1L, "蒙多", 33, "一个从菜刀中明悟哲理的祖安人", null);
        List<Author> authors2 = getAuthors();
        Optional<List<Author>> authorOptional = Optional.of(authors2);
        authorOptional.ifPresent(i -> System.out.println(i));
        if (author != null) {
            Optional.of(authors2);
        } else {
            // 如果一个方法的返回值类型是Optional类型。而如果我们经判断发现某次计算得到的返回值为null，这个时候就需要把null封装成Optional对象返回。这时则可以使用**Optional**的**静态方法empty**来进行封装。
            Optional.empty();
        }
    }

    @Test
    public void testFlatMap() {
        List<Author> authors = getAuthors();
        List<Book> collect = authors.parallelStream().flatMap(item -> item.getBooks().stream()).collect(Collectors.toList());
        Integer reduce = authors.stream().map(item -> item.getAge()).reduce(0, Integer::sum);
        Integer integer = authors.stream().map(Author::getAge).max(Comparator.comparingInt(o -> o)).get();

        System.out.println(integer);
        System.out.println(collect);
    }

    // 基本数据类型优化
    // ​
    //	即使我们操作的是整数小数，但是实际用的都是他们的包装类。JDK5中引入的自动装箱和自动拆箱让我们在使用对应的包装类时就好像使用基本数据类型一样方便。但是你一定要知道装箱和拆箱肯定是要消耗时间的。虽然这个时间消耗很下。但是在大量的数据不断的重复装箱拆箱的时候，你就不能无视这个时间损耗了。
    //         ​	所以为了让我们能够对这部分的时间消耗进行优化。Stream还提供了很多专门针对基本数据类型的方法。
    @Test
    public void test29() {
        // 例如：mapToInt,mapToLong,mapToDouble,flatMapToInt,flatMapToDouble等。
        List<Author> authors = getAuthors();
        authors.stream()
                .map(author -> author.getAge())
                .map(age -> age + 10)
                .filter(age -> age > 18)
                .map(age -> age + 2)
                .forEach(System.out::println);
        // 拆箱
        authors.stream()
                .mapToInt(author -> author.getAge())
                .map(age -> age + 10)
                .filter(age -> age > 18)
                .map(age -> age + 2)
                .forEach(System.out::println);
    }

    // 并行流（优化性能效率）
    @Test
    public void test30() {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        // .parallel()开启多线程优化
        Integer sum =stream
                        .peek(num -> System.out.println(num +":线程名字："+ Thread.currentThread().getName()))
                        .filter(num -> num > 5)
                        .reduce((result, ele) -> result + ele)
                        .get(); // 取得值
        System.out.println(sum);
    }

    // 并行流（优化性能效率）
    @Test
    public void test31() {
        List<Author> authors = getAuthors();
        // parallelStream 多线程流
        authors.parallelStream()
                .map(author -> author.getAge())
                .map(age -> age + 10)
                .filter(age -> age > 18)
                .map(age -> age + 2)
                .forEach(System.out::println);
    }

    // 26
    //         26
    //         45
    //         27
    private static List<Author> getAuthors() {
        // 数据初始化
        Author author = new Author(1L, "蒙多", 38, "一个从菜刀中明悟哲理的祖安人", null);
        Author author2 = new Author(2L, "亚拉索", 15, "狂风也追逐不上他的思考速度", null);
        Author author3 = new Author(3L, "易", 14, "是这个世界在限制他的思维", null);
        Author author4 = new Author(3L, "易", 14, "是这个世界在限制他的思维", null);

        // 书籍列表
        List<Book> books1 = new ArrayList<>();
        List<Book> books2 = new ArrayList<>();
        List<Book> books3 = new ArrayList<>();

        books1.add(new Book(1L, "刀的两侧是光明与黑暗", "哲学,爱情", 88, "用一把刀划分了爱恨"));
        books1.add(new Book(2L, "一个人不能死在同一把刀下", "个人成长,爱情", 99, "讲述如何从失败中明悟真理"));

        books2.add(new Book(3L, "那风吹不到的地方", "哲学", 85, "带你用思维去领略世界的尽头"));
        books2.add(new Book(3L, "那风吹不到的地方", "哲学", 85, "带你用思维去领略世界的尽头"));
        books2.add(new Book(4L, "吹或不吹", "爱情,个人传记", 56, "一个哲学家的恋爱观注定很难把他所在的时代理解"));

        books3.add(new Book(5L, "你的剑就是我的剑", "爱情", 56, "无法想象一个武者能对他的伴侣这么的宽容"));
        books3.add(new Book(6L, "风与剑", "个人传记", 100, "两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢？"));
        books3.add(new Book(6L, "风与剑", "个人传记", 100, "两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢？"));

        author.setBooks(books1);
        author2.setBooks(books2);
        author3.setBooks(books3);
        author4.setBooks(books3);

        List<Author> authorList = new ArrayList<>(Arrays.asList(author, author2, author3, author4));
        return authorList;
    }
}
